Strict

Import mojo
Import agfx


Const GM_LINE:Int  = 0 
Const GM_LINES:Int = 1 
Const GM_SPLINES:Int = 3

'http://www.cubic.org/docs/hermite.htm'

'|--------------------------------------------------------------------------------
'| TGesture Point
'|--------------------------------------------------------------------------------
Class TGesturePoint

	'Field x:Float
	'Field y:Float
	
	Field position:TVector2
	
	'|--------------------------------------------------------------------------------
	'| Constructor
	'|--------------------------------------------------------------------------------
	Method New()
		'Self.x = 0
		'Self.y = 0 
		Self.position = New TVector2(0,0)
	End

	'|--------------------------------------------------------------------------------
	'| DRAW
	'|--------------------------------------------------------------------------------
	Method Draw:Void()
		SetColor(255,255,255)
		DrawOval(Self.position.x,Self.position.y,3,3)
	End

End

'|--------------------------------------------------------------------------------
'| TGesture segment
'|--------------------------------------------------------------------------------
Class TGesturePointSegment

	Field a:TGesturePoint
	Field b:TGesturePoint
	Field length:Float

	'|--------------------------------------------------------------------------------
	'| Constructor
	'|--------------------------------------------------------------------------------
	Method New()
		Self.a = New TGesturePoint
		Self.b = New TGesturePoint
		Self.length = 0
	End

End

'|--------------------------------------------------------------------------------
'| TGesture RECORDER
'|--------------------------------------------------------------------------------
Class TGestureRecorder

	Private

	Field step_pause:Int
	Field cstep:Int
	Field bSmooth:Bool
	Field delta:Float = 0.2	
	Field time_start:Int = 0
	Field time_end:Int = 0

	Public

	Field points:List<TGesturePoint>
	Field pointArray:TGesturePoint[]
	Field segments:List<TGesturePointSegment>
	Field count:Int
	Field draw_on:Bool = False
	Field mode:Int = GM_LINES
	Field length:Float = 0

	'|--------------------------------------------------------------------------------
	'| Constructor
	'|--------------------------------------------------------------------------------
	Method New()
		Self.points = New List<TGesturePoint>
		Self.segments = New List<TGesturePointSegment>
		Self.step_pause = 1
		Self.cstep = 0
		Self.count = 0
		Self.bSmooth=False
	End

	'|--------------------------------------------------------------------------------
	'| SetDelta
	'|--------------------------------------------------------------------------------
	Method SetDelta:Void(d:Float)
		Self.delta = d
	End

	'|--------------------------------------------------------------------------------
	'| Update
	'|--------------------------------------------------------------------------------
	Method Update:Void()

		if MOUSE.Hit() Then
			Self.time_start = Millisecs()
			Local p:TGesturePoint = New TGesturePoint
			Self.count = 0
			Self.points.Clear()
			p.position.x = MOUSE.x
			p.position.y = MOUSE.y
			Self.points.AddLast(p)
			Self.count = Self.count + 1
			
		End

		' LINES mode
		If Self.mode = GM_LINES Or Self.mode = GM_SPLINES Then
			if MOUSE.Down() Then
				Self.cstep = Self.cstep + 1*AppSpeed()
				
				if Self.cstep> Self.step_pause Then
					Local p:TGesturePoint = New TGesturePoint
					p.position.x = MOUSE.x
					p.position.y = MOUSE.y
					Self.points.AddLast(p)
					Self.count = Self.points.Count()
					Self.cstep = 0
				End
			End
		End

		if MOUSE.Up() Then

			Self.time_end = Millisecs()

			If Self.mode = GM_SPLINES Then			
				Self.SmoothPath()				
			End
			
			If Self.mode = GM_LINE Then			
				Local p:TGesturePoint = New TGesturePoint
				p.position.x = MOUSE.x
				p.position.y = MOUSE.y
				Self.points.AddLast(p)
				Self.count = Self.count + 1			
			End
			
			Self.pointArray = Self.GetAsArray()
			
			Self.GenerateSegments()
			
			' User defined event via Extends of this class
			Self.OnRelease()
			
		End
	End

	'|--------------------------------------------------------------------------------
	'| Render
	'|--------------------------------------------------------------------------------
	Method Render:Void()
	
		If Self.draw_on Then
			Local a:Int=0
			Local px:Int
			Local py:Int
			Local p:TGesturePoint
			SetColor(255,255,255)

			For p = eachin Self.points
				
				If a=0 Then
					px =  p.position.x
					py = p.position.y
					a = 1
				Else
					SetColor(32,32,32)
					DrawLine(px,py,p.position.x,p.position.y)
					px = p.position.x
					py = p.position.y
				End if

				p.Draw()

			Next
		End
	End

	Private

	'|--------------------------------------------------------------------------------
	'| Smooth path
	'|--------------------------------------------------------------------------------
	Method SmoothPath:Void()

		Local new_path:List<TGesturePoint> = New List<TGesturePoint>
		Local b_points:FLoat[8]
		Local bezier:TBezier
		Local i:Int=0
		Local t:Float
		Local n:Int =0
		Local tn:Int =8
		Local l:Float = 0
		Local c:Int = 0
		Local p:TGesturePoint
		Local pp:TGesturePoint

		'Cal subdivision
		
		' get list of points'
		For p = eachin Self.points
		
			b_points[n+i]= p.position.x
			b_points[n+i+1]= p.position.y
			i=i+2

			' smooth first 4 points'
			If i=tn Then
				
				bezier = New TBezier(b_points,Self.delta) 

				t = 0
				While t<1
					t+=bezier.inc	
					If t>1 t=1
					
					'calculate the position of the corresponding point on the curve
					pp = New TGesturePoint
					pp.position.x = bezier.xpos(t)
					pp.position.y = bezier.ypos(t)
					new_path.AddLast(pp)
					c = c + 1
				Wend

				
				tn=6
				i = 0
				n = 2

				b_points[0] = b_points[6]
				b_points[1] = b_points[7]

			
			End
		Next

		Self.points.Clear()
		Self.points = new_path
		Self.count =  Self.points.Count()
		
		
	End
	
	'|--------------------------------------------------------------------------------
	'| generate segments
	'|--------------------------------------------------------------------------------
	Method GenerateSegments:Void()
		
		'Clean segments
		
		Self.segments.Clear()
		
		'Generate segments
		
		Local seg:TGesturePointSegment = New TGesturePointSegment()
		Self.length = 0.0		
				
		For Local i:Int=1 To Self.count-1
		
			
			Local seg:TGesturePointSegment = New TGesturePointSegment()
			
			seg.a = New TGesturePoint()		
			seg.a = Self.pointArray[i-1]
			
			seg.b = New TGesturePoint()
			seg.b = Self.pointArray[i]			
			
			seg.length = seg.a.position.DistanceTo(seg.b.position)
			Self.length = Self.length + seg.length
			Self.segments.AddLast(seg)
		Next		
	End Method

	'|--------------------------------------------------------------------------------
	'| Get as Gesture point Array
	'|--------------------------------------------------------------------------------
	Method GetAsArray:TGesturePoint[]()
	
		Local arr:TGesturePoint[] = New TGesturePoint[Self.count]
		Local i:Int=0
		Local p:TGesturePoint

		For p = eachin Self.points
			arr[i] = New TGesturePoint()
			arr[i].position.Set(p.position)
			
			i+=1		
		Next
	
		Return arr
		
	End Method
	
	'|--------------------------------------------------------------------------------
	'| Get Gesture speed
	'|--------------------------------------------------------------------------------
	Method GetSpeed:Float()
		Local time:Int = Self.time_end - Self.time_start
		Local speed:Float = Self.length / time
		Return speed
	End Method

	'|--------------------------------------------------------------------------------
	'| Get Gesture speed
	'|--------------------------------------------------------------------------------
	Method GetTime:Int()
		Local time:Int = Self.time_end - Self.time_start		
		Return time
	End Method

	'|--------------------------------------------------------------------------------
	'| Abstract method for OnRelease mouse
	'|--------------------------------------------------------------------------------
	Method OnRelease:Void() Abstract
	
	
End


